//# include "InventorAll.h"
#include "GlobalHead.h"


#include <QSessionManager>

#include "Gui_Application.h"
#include "MainWindow.h"
#include "Gui/Gui_Document.h"
#include "Workbench.h"
#include "WorkbenchManager.h"
#include "View.h"
#include "MenuManager.h"
#include "MDIView.h"
#include "Command.h"
#include "FileInfo.h"
#include "View3DInventor.h"
#include "ViewProvider.h"
#include "ViewProviderDocumentObject.h"
#include "ViewProviderGeometryObject.h"
#include "ToolsCallBack.h"
#include "ToolsCallback_Curve.h"
#include "BitmapFactory.h"
#include "SoFCDB.h"
#include "ViewProviderVRMLObject.h"
#include "part/AppPartGui.h"
#include "CompleteGui.h"
#include "ToolsCallback_Curve.h"
#include "ToolsCallback_Camera.h"



using namespace Gui;
using namespace std;

// Pimpl class
struct ApplicationP
{
	ApplicationP() : 
activeDocument(0L), 
isClosing(false), 
startingUp(true) 
///_stderr(0)
{
	// create the macro manager
	//macroMngr = new MacroManager();
}

~ApplicationP()
{
	//delete macroMngr;
}

/// list of all handled documents
std::map<const App::Document*, Gui::Gui_Document*> documents;
/// Active document
Gui::Gui_Document*   activeDocument;
//MacroManager*  macroMngr;
/// List of all registered views
std::list<Gui::BaseView*> passive;
bool isClosing;
bool startingUp;
/// Handles all commands 
CommandManager commandManager;
//PyObject *_stderr;
};



class GUIApplication : public QApplication
{
public:

	GUIApplication(int& argc,char** argv)
		:QApplication(argc,argv)
	{

	}

	bool notify(QObject* receiver,QEvent* event)
	{
		if (!receiver && event)
		{
			return false;
		}
		 return QApplication::notify(receiver,event);

		if (receiver && event) {
			std::stringstream dump;
			dump << "The event type " << (int)event->type() << " was sent to "
				<< receiver->metaObject()->className() << "\n";
			dump << "Object tree:\n";
			if (receiver->isWidgetType()) {
				QWidget* w = qobject_cast<QWidget*>(receiver);
				while (w) {
					dump << "\t";
					dump << w->metaObject()->className();
					QString name = w->objectName();
					if (!name.isEmpty())
						dump << " (" << (const char*)name.toUtf8() << ")";
					w = w->parentWidget();
					if (w)
						dump << " is child of\n";
				}
				std::string str = dump.str();
				Base::Console().Log("%s",str.c_str());
			}
		}

		return true;

	}

	void commitData(QSessionManager& manager)
	{
		if(manager.allowsInteraction())
		{
			if (!getMainWindow()->close()) {
				// cancel the shutdown
				manager.release();
				manager.cancel();
			}
		}
		else
		{
			GetApp_Application().closeAllDocuments();
			getMainWindow()->close();
		}
	}
};


Gui_Application* Gui_Application::instance = 0;
Gui_Application::Gui_Application(void)
{

	GetApp_Application().signalNewDocument.connect(boost::bind(&Gui_Application::slotNewDocument, this, _1));
	GetApp_Application().signalDeleteDocument.connect(boost::bind(&Gui_Application::slotDeleteDocument, this, _1));
	GetApp_Application().signalRenameDocument.connect(boost::bind(&Gui_Application::slotRenameDocument, this, _1));
	GetApp_Application().signalActiveDocument.connect(boost::bind(&Gui_Application::slotActiveDocument, this, _1));
	GetApp_Application().signalRelabelDocument.connect(boost::bind(&Gui_Application::slotRelabelDocument, this, _1));

	//GetApp_Application().signalUpdataDoumentObject.connect(boost::bind(&Gui_Application::slotUpdataDoumentObject,this,_1));

	GetApp_Application().signalNewScene.connect(boost::bind(&Gui_Application::slotNewScene, this, _1));


	 d = new ApplicationP;
	  instance  = this;
	  createStandardOperations();		
}

Gui_Application::~Gui_Application(void)
{
	Gui::CompleteGui::destruct();
	//CompleteGui::destruct();
	toolsManager::Intance()->destruct();
	WorkbenchManager::destruct();
	SelectionSingleton::destruct();
// 	Translator::destruct();
// 	WidgetFactorySupplier::destruct();
	BitmapFactoryInst::destruct();
	delete d;
	instance = 0;

}

void Gui_Application::createStandardOperations()
{
	CreateStdCommands();
	CreateDocCommands(); 
// 	CreateFeatCommands();
// 	CreateMacroCommands();
 	CreateViewStdCommands();
 	CreateWindowStdCommands();
// 	CreateTestCommands();
	CreateToolsCommands();
	CreateToolsExCommands();
	CreateCompleteCommands();
}

Gui::Gui_Document*Gui_Application::getActiveDocument() const
{
	return d->activeDocument;
}

Gui::Gui_Document*Gui_Application::getDocument(const char* name) const
{
	return d->activeDocument;
}

Gui::Gui_Document*Gui_Application::getDocument(const App::Document* pDoc) const
{
  return d->activeDocument;
}
void Gui_Application::open(const char* FileName, const char* Module)
{

 	//WaitCursor wc;
  	Base::FileInfo File(FileName);
  	string te = File.extension();
 
 	// if the active document is empty and not modified, close it
 	// in case of an automatically created empty document at startup
//  	App::Document* act = GetApp_Application().getActiveDocument();
//  	Gui_Document* gui = this->getDocument(act);
//  	if (act && act->countObjects() == 0 && gui && gui->isModified() == false){
//  		Command::doCommand(Command::App, "App.closeDocument('%s')", act->getName());
//  		qApp->processEvents(); // an update is needed otherwise the new view isn't shown
//  	}
 
 	if (Module != 0) {
 		// issue module loading
 		//Command::doCommand(Command::App, "import %s", Module);

 			// load the file with the module
 			//Command::doCommand(Command::App, "App.openDocument()", File.filePath().c_str());
		   Command::doCommand((CommandStringFunction)App_Application::openDocument_s,File.filePath().c_str());
 			// ViewFit
//  			if (!File.hasExtension("FCStd") && sendHasMsgToActiveView("ViewFit"))
//  				//Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().fitAll()");
//  				Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
//  			// the original file name is required
//  			getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));

 	}
 	else {

 		QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
 			QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(FileName)));
 		return;
 	}
}

void Gui_Application::importFrom(const char* FileName, const char* DocName, const char* Module)
{
// 	WaitCursor wc;
 	Base::FileInfo File(FileName);
 	std::string te = File.extension();
// 
 	if (Module != 0) {
// 		// issue module loading
// 		Command::doCommand(Command::App, "import %s", Module);
// 
// 		try {
// 			// load the file with the module
// 			if (File.hasExtension("FCStd")) {
// 				Command::doCommand(Command::App, "%s.open(\"%s\")"
// 					, Module, File.filePath().c_str());
// 				if (activeDocument())
// 					activeDocument()->setModified(false);
// 			}
// 			else {
// 				Command::doCommand(Command::App, "App.insertObject()"
//  					, File.filePath().c_str());
				Command::doCommand((CommandStringFunction)App_Application::importObject,File.filePath().c_str());
// 				if (getDocument(DocName))
// 					getDocument(DocName)->setModified(true);
// 			}
// 
// 			// the original file name is required
// 			getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
// 		}
// 		catch (const Base::PyException& e){
// 			// Usually thrown if the file is invalid somehow
// 			e.ReportException();
// 		}
	}
 	else {
//		wc.restoreCursor();
 		QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
 			QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(te.c_str())));
 		//wc.setWaitCursor();
 	}
}

void Gui_Application::exportTo(const char* FileName, const char* DocName, const char* Module)
{
// 	WaitCursor wc;
// 	Base::FileInfo File(FileName);
// 	std::string te = File.extension();
// 
// 	if (Module != 0) {
// 		try {
// 			std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
// 				(App::DocumentObject::getClassTypeId(),DocName);
// 			if (sel.empty()) {
// 				App::Document* doc = App::GetApplication().getDocument(DocName);
// 				sel = doc->getObjectsOfType(App::DocumentObject::getClassTypeId());
// 			}
// 
// 			std::stringstream str;
// 			str << "__objs__=[]" << std::endl;
// 			for (std::vector<App::DocumentObject*>::iterator it = sel.begin(); it != sel.end(); ++it) {
// 				str << "__objs__.append(FreeCAD.getDocument(\"" << DocName << "\").getObject(\""
// 					<< (*it)->getNameInDocument() << "\"))" << std::endl;
// 			}
// 
// 			str << "import " << Module << std::endl;
// 			str << Module << ".export(__objs__,\"" << File.filePath() << "\")" << std::endl;
// 			str << "del __objs__" << std::endl;
// 
// 			std::string code = str.str();
// 			// the original file name is required
// 			if (runPythonCode(code.c_str(), false))
// 				getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
// 		}
// 		catch (const Base::PyException& e){
// 			// Usually thrown if the file is invalid somehow
// 			e.ReportException();
// 		}
// 	}
// 	else {
// 		wc.restoreCursor();
// 		QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
// 			QObject::tr("Cannot save to unknown filetype: %1").arg(QLatin1String(te.c_str())));
// 		wc.setWaitCursor();
// 	}
}



void Gui_Application::setActiveDocument(Gui_Document* pcDocument)
{
	if (d->activeDocument == pcDocument)
		return; // nothing needs to be done
	d->activeDocument = pcDocument;
  // GetApp_Application().setActiveDocument(NULL);

	for(list<Gui::BaseView*>::iterator It=d->passive.begin();It!=d->passive.end();It++)
		(*It)->setDocument(pcDocument);
  

}

Gui::Gui_Document* Gui_Application::activeDocument(void) const
{
	return d->activeDocument;
}

void Gui_Application::slotNewScene(const App::Document& pDoc )
{
   activeDocument()->clearScene();
}
void Gui_Application::slotNewDocument(const App::Document& Doc)
{
	Gui::Gui_Document* pDoc = new Gui::Gui_Document(const_cast<App::Document*>(&Doc),this);
	d->documents[&Doc] = pDoc;
	signalNewDocument(*pDoc);
	pDoc->createView("View3DIv");
	qApp->processEvents(); // make sure to show the window stuff on the right place

	setActiveDocument(pDoc);

}

void Gui_Application::slotDeleteDocument(const App::Document& Doc)
{
	std::map<const App::Document*, Gui::Gui_Document*>::iterator doc = d->documents.find(&Doc);
	if (doc == d->documents.end()) {
	//	Base::Console().Log("GUI document '%s' already deleted\n", Doc.getName());
		return;
	}

	// We must clear the selection here to notify all observers
	Gui::Selection().clearSelection(doc->second->getDocument()->getName());
	signalDeleteDocument(*doc->second);

	// If the active document gets destructed we must set it to 0. If there are further existing documents then the 
	// view that becomes active sets the active document again. So, we needn't worry about this.
	if (d->activeDocument == doc->second)
		setActiveDocument(0);

	// For exception-safety use a smart pointer
	auto_ptr<Gui::Gui_Document> delDoc (doc->second);
	d->documents.erase(doc);
}

void Gui_Application::slotRelabelDocument(const App::Document& Doc)
{
	std::map<const App::Document*, Gui::Gui_Document*>::iterator doc = d->documents.find(&Doc);
#ifdef FC_DEBUG
	assert(doc!=d->documents.end());
#endif

	signalRelabelDocument(*doc->second);
	//doc->second->onRelabel();
}

void Gui_Application::slotRenameDocument(const App::Document& Doc)
{
	std::map<const App::Document*, Gui::Gui_Document*>::iterator doc = d->documents.find(&Doc);
#ifdef FC_DEBUG
	assert(doc!=d->documents.end());
#endif

	signalRenameDocument(*doc->second);
}

void Gui_Application::slotActiveDocument(const App::Document& Doc)
{
	std::map<const App::Document*, Gui::Gui_Document*>::iterator doc = d->documents.find(&Doc);
	// this can happen when closing a document with two views opened
	if (doc != d->documents.end())
		signalActiveDocument(*doc->second);
}


void Gui_Application::tryClose(QCloseEvent * e)
{
	if (d->documents.size() == 0) {
		e->accept();
	}
	else {
		// ask all documents if closable
		std::map<const App::Document*, Gui_Document*>::iterator It;
		for (It = d->documents.begin();It!=d->documents.end();It++) {
			// a document may have several views attached, so ask it directly
#if 0
			MDIView* active = It->second->getActiveView();
			e->setAccepted(active->canClose());
#else
			e->setAccepted(It->second->canClose());
#endif
			if (!e->isAccepted())
				return;
		}
	}

	// ask all passive views if closable
	for (std::list<Gui::BaseView*>::iterator It = d->passive.begin();It!=d->passive.end();It++) {
		e->setAccepted((*It)->canClose());
		if (!e->isAccepted())
			return;
	}

	if (e->isAccepted()) {
		d->isClosing = true;

		std::map<const App::Document*, Gui_Document*>::iterator It;

		//detach the passive views
		//SetActiveDocument(0);
		std::list<Gui::BaseView*>::iterator itp = d->passive.begin();
		while (itp != d->passive.end()) {
			(*itp)->onClose();
			itp = d->passive.begin();
		}

		// remove all documents
		size_t cnt = d->documents.size();
		while (d->documents.size() > 0 && cnt > 0) {
			// destroys also the Gui document
			It = d->documents.begin();
			GetApp_Application().closeDocument(It->second->getDocument()->getName());
			--cnt; // avoid infinite loop
		}
	}
}

void Gui_Application::slotUpdataDoumentObject(const App::DocumentObject& obj)
{
   getActiveDocument()->updataDocumentObjectMaterial(&obj);
}


void Gui_Application::initTypes()
{
	Gui::BaseView::init();
	MDIView::init();

	Gui::View3DInventor                         ::init();
	// View Provider
	Gui::ViewProvider                           ::init();
//	Gui::ViewProviderExtern                     ::init();
	Gui::ViewProviderDocumentObject             ::init();
//	Gui::ViewProviderFeature                    ::init();
	//Gui::ViewProviderDocumentObjectGroup        ::init();
	Gui::ViewProviderGeometryObject             ::init();
	//Gui::ViewProviderInventorObject             ::init();
	
	Gui::Tools                                ::init();
	Gui::movetool                             ::init();
	Gui::rotatetool                           ::init();
	Gui::scaletool                            ::init();

	Gui::lineTool                             ::init();
	Gui::CircleTool                           ::init();
	Gui::PathCamera                           ::init();
	Gui::FreeCamera                           ::init();

	Workbench                                 ::init();
	StdWorkbench                               ::init();
	BlankWorkBench                             ::init();
	NoneWorkbench                              ::init();
	TestWorkBench                              ::init();
	CompleteWorkbench                           ::init();
	StartWorkbench                              ::init();
	PartWorkbench                               ::init();


	CompleteGui::Instatnce()->initCompleteGui();






}

void Gui_Application::initApplication(void)
{
   initTypes();
   Q_INIT_RESOURCE(resource);
}

void Gui_Application::runApplication(void)
{
	static int v = 0;
    GUIApplication mainApp(v,0);

	mainApp.setWindowIcon(BitmapFactory().pixmap(App_Application::Config()["AppIcon"].c_str()));
	mainApp.setApplicationName(QString::fromAscii(GetApp_Application().getExecutableName()));

	QString plugin;
	plugin = QString::fromUtf8(GetApp_Application().GetHomePath());
	plugin += QLatin1String("/plugins");
	QCoreApplication::addLibraryPath(plugin);

   Gui_Application app;
   MainWindow mw;
   mw.setWindowTitle(QString::fromAscii("test"));

   SoFCDB::init();;

   Gui_Application::initApplications();

   mainApp.setActiveWindow(&mw);

//    std::string start = App_Application::Config()["StartWorkbench"];
//   // Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
//    start = GetApp_Application().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
// 	   GetASCII("AutoloadModule", start.c_str());

   QStringList wb = app.workbenches();
//    if (!wb.contains(QString::fromAscii(start.c_str()))) {
// 	   start = App_Application::Config()["StartWorkbench"];
// 	  GetApp_Application().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
// 		   SetASCII("AutoloadModule", start.c_str());
//    }
   app.activateWorkbench("StartWorkbench");



   mw.loadWindowSettings();

   mw.showTipOfTheDay();

   instance->d->startingUp = false;

   QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
  

   mainApp.exec();

	
}

QStringList Gui_Application::workbenches(void)const
{
// 	const std::map<std::string,std::string>& config = App_Application::Config();
// 	std::map<std::string, std::string>::const_iterator ht = config.find("HiddenWorkbench");
// 	std::map<std::string, std::string>::const_iterator et = config.find("ExtraWorkbench");
// 	std::map<std::string, std::string>::const_iterator st = config.find("StartWorkbench");
// 	const char* start = (st != config.end() ? st->second.c_str() : "<none>");
// 	QStringList hidden, extra;
// 	if (ht != config.end()) { 
// 		QString items = QString::fromAscii(ht->second.c_str());
// 		hidden = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
// 		if (hidden.isEmpty())
// 			hidden.push_back(QLatin1String(""));
// 	}
// 	if (et != config.end()) { 
// 		QString items = QString::fromAscii(et->second.c_str());
// 		extra = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
// 		if (extra.isEmpty())
// 			extra.push_back(QLatin1String(""));
// 	}

	   QStringList wb;

	   wb.push_back((QString::fromAscii("StartWorkbench")));
	   wb.push_back((QString::fromAscii("CompleteWorkbench")));
	   wb.push_back((QString::fromAscii("NoneWorkbench")));
	   wb.push_back((QString::fromAscii("PartWorkbench")));

	   return wb;


}


QPixmap Gui_Application::workbenchIcon(const QString&) const
{
    return QPixmap();
}
QString Gui_Application::workbenchToolTip(const QString&) const
{
     return QString();
}
QString Gui_Application::workbenchMenuText(const QString& wb) const
{
     if(strcmp(wb.toAscii(),"StartWorkbench") ==0)
		 return QString::fromAscii("Start");
	 else if(strcmp(wb.toAscii(),"CompleteWorkbench") == 0)
		 return QString::fromAscii("Complete");
	 else if(strcmp(wb.toAscii(),"NoneWorkbench") == 0)
		 return QString::fromAscii("None");
	 else if(strcmp(wb.toAscii(),"PartWorkbench") == 0)
		 return QString::fromAscii("Part");
     return QString();
}

void Gui_Application::setupContextMenu(const char* recipient, MenuItem*) const
{

}

bool Gui_Application::AddDataObject(const char* objName)
{
	return true;
}

bool Gui_Application::sendMsgToActiveView(const char* pMsg, const char** ppReturn)
{
	MDIView* pView = getMainWindow()->activeWindow();
	return pView ? pView->onMsg(pMsg,ppReturn) : false;
  //return true;
}
/// send Messages test to the active view
bool  Gui_Application::sendHasMsgToActiveView(const char* pMsg)
{
  return true;
}
/// Attach a view (get called by the FCView constructor)
void  Gui_Application::attachView(Gui::BaseView* pcView)
{
    d->passive.push_back(pcView);
}
/// Detach a view (get called by the FCView destructor)
void  Gui_Application::detachView(Gui::BaseView* pcView)
{
   d->passive.remove(pcView);
}
/// get called if a view gets activated, this manage the whole activation scheme
void  Gui_Application::viewActivated(MDIView* pcView)
{

}
/// call update to all docuemnts an all views (costly!)
void  Gui_Application::onUpdate(void)
{

}
/// call update to all views of the active document
void  Gui_Application::updateActive(void)
{

}
//@}

Gui_Application* Gui_Application::GetInstance()
{

	if(instance == NULL)
	{
		instance = new Gui_Application;
	}

	return instance;
}
bool Gui_Application::activateWorkbench(const char* name)
{
	bool ok = false;
	Workbench* oldWb = WorkbenchManager::instance()->active();
	if (oldWb && oldWb->name() == name)
	return false; // already active 

	else if(WorkbenchManager::instance()->activate(name,name))
	{
		getMainWindow()->activateWorkbench(QString::fromAscii(name));
		this->signalActivateWorkbench(name);
		ok = true;
	}

	if(oldWb)
		oldWb->deactivated();

	Workbench* newWb = WorkbenchManager::instance()->active();
	if(newWb)
		newWb->activated();


	return ok;
}

void Gui_Application::AddWorkbench(const std::string& name)
{
    instance->signalAddWorkbench(name.c_str());
}
void Gui_Application::ActivateWorkbench(const char* wbname)
{

   instance->activateWorkbench(wbname);
// 	bool ok = false;
// 	Workbench* oldWb  = WorkbenchManager::instance()->active();
// 	if(oldWb && oldWb->name() == name)
// 		return false;
// 
// 	//if(name == "CompleteWorkBench")
// 	//{
// 		Workbench* wb = WorkbenchManager::instance()->createWorkbench(name,name);
// 		wb->Initialize();
// 	//}
// 	Workbench* curWb = WorkbenchManager::instance()->active();
// 	if (curWb && curWb->name() == name)
// 		ok = true; // already active
// 	else if(WorkbenchManager::instance()->activate(name,name))
// 	{
//       getMainWindow()->activateWorkbench(QString::fromAscii(name.c_str()));
// 	  this->signalActivateWorkbench(name.c_str());
// 	  ok = true;
// 	}
// 
// 	if (oldWb)
// 		oldWb->deactivated();
// 
// 	Workbench* newWb = WorkbenchManager::instance()->active();
// 	if (newWb)
// 		newWb->activated();


}
void Gui_Application::RemoveWorkbench(std::string& name)const
{
   instance->signalRemoveWorkbench(name.c_str());
}

void Gui_Application::initApplications()
{
	//const std::string wStr = "NoneWorkbench";
	//Gui_Application::GetInstance()->activateWorkbench(wStr.c_str());

	//Gui_Application::GetInstance()->ActivateWorkbench("CompleteWorkBench");
}

Gui::ViewProvider* Gui_Application::getViewProvider(App::DocumentObject* obj) const
{
	App::Document* doc = obj->getDocument();
	if (doc) {
		Gui_Document* gui = getDocument(doc);
		if (gui) {
			ViewProvider* vp = gui->getViewProvider(obj);
			return vp;
		}
	}

	return 0;
}


CommandManager& Gui_Application::commandManager(void)
{
   return d->commandManager;
}


